import utils from '../utils'

const UNITS_LOOKUP = {
  m: 'meters',
  meter: 'meters',
  meters: 'meters',
  mi: 'miles',
  mile: 'miles',
  miles: 'miles',
  km: 'kilometers',
  ft: 'feet',
  feet: 'feet'
}
// throws an error if measure is non-parsable
function parseMeasure(m) {
  const o = parseMeasure2(m)
  if (isNaN(o.value)) {
    utils.error(`'Invalid parameter:${m}`)
  }
  return o
}
// returns NaN value if value is non-parsable
function parseMeasure2(m) {
  const s = utils.isString(m) ? m : ''
  const match = /(sq|)([a-z]+)(2|)$/i.exec(s) // units rxp
  const o = {}
  if (utils.isNumber(m)) {
    o.value = m
  } else if (s === '') {
    o.value = NaN
  } else if (match) {
    o.units = UNITS_LOOKUP[match[2].toLowerCase()]
    o.areal = !!(match[1] || match[3])
    o.value = Number(s.substring(0, s.length - match[0].length))
    if (!o.units && !isNaN(o.value)) {
      // throw error if string contains a number followed by unrecognized units string
      utils.error(`Unknown units:${match[0]}`)
    }
  } else {
    o.value = Number(s)
  }
  return o
}
// From pj_units.js in mapshaper-proj
const TO_METERS = {
  meters: 1,
  kilometers: 1000,
  feet: 0.3048, // International Standard Foot
  miles: 1609.344 // International Statute Mile
}
// Return coeff. for converting a distance measure to dataset coordinates
// @paramUnits: units code of distance param, or null if units are not specified
// @crs: Proj.4 CRS object, or null (unknown latlong CRS);
//
function getIntervalConversionFactor(paramUnits, crs) {
  let fromParam = 0
  let fromCRS = 0
  let k = 0

  if (crs) {
    if (crs.is_latlong) {
      // calculations on latlong coordinates typically use meters
      fromCRS = 1
    } else if (crs.to_meter > 0) {
      fromCRS = crs.to_meter
    } else {
      utils.error('Invalid CRS')
    }
  }
  if (paramUnits) {
    fromParam = TO_METERS[paramUnits]
    if (!fromParam) utils.error(`'Unknown units:', paramUnits`)
  }

  if (fromParam && fromCRS) {
    // known param units, known CRS conversion
    k = fromParam / fromCRS
  } else if (!fromParam && !fromCRS) {
    // unknown param units, unknown (projected) CRS -- no scaling
    k = 1
  } else if (fromParam && !fromCRS) {
    // known param units, unknown CRS -- error condition, not convertible
    // stop('Unable to convert', paramUnits, 'to unknown coordinates')
    utils.error(`Unable to convert ${paramUnits} to unknown coordinates`)
  } else if (!fromParam && fromCRS) {
    // unknown param units, known CRS -- assume param in meters (bw compatibility)
    k = 1 / fromCRS
  }
  return k
}
// Same as convertDistanceParam(), except:
//   in the case of latlong datasets, coordinates are unitless (instead of meters),
//   and parameters with units trigger an error
function convertIntervalParam(opt, crs) {
  const o = parseMeasure(opt)
  const k = getIntervalConversionFactor(o.units, crs)
  if (o.units && crs && crs.is_latlong) {
    utils.error('Parameter does not support distance units with latlong datasets')
  }
  if (o.areal) {
    utils.error(`Expected a distance, received an area:${opt}`)
  }
  return o.value * k
}

export default {
  convertIntervalParam,
}